package com.hh.flow.utils;

import com.hh.common.utils.AssertUtil;
import com.hh.common.utils.uuid.IdUtils;
import com.hh.flow.constant.FlowConstant;
import com.hh.flow.domain.FlowDefinition;
import com.hh.flow.domain.FlowNode;
import com.hh.flow.domain.FlowSkip;
import com.hh.flow.enums.NodeType;
import com.hh.flow.dto.FlowCombine;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * 流程配置帮助类
 *
 * @author zhoukai
 */
public class FlowConfigUtil {
    /**
     * 无参构造
     */
    private FlowConfigUtil() {

    }


    /**
     * 读取配置
     *
     * @throws Exception
     */
    public static FlowCombine registerConfig() throws Exception {
        FlowCombine combine = readFlowJson();
        return combine;
    }

    private static FlowCombine readFlowJson() {
        // 获取流程
        FlowDefinition definition = getFlowJson();
        FlowCombine combine = new FlowCombine();
        //流程定义
        combine.setAllDefinition(definition);
        //所有的流程结点
        List<FlowNode> allNodes = combine.getAllNodes();
        //所有的流程连线
        List<FlowSkip> allSkips = combine.getAllSkips();

        String flowName = definition.getFlowName();
        AssertUtil.isBlank(definition.getFlowCode(), "【" + flowName + "】流程flowCode为空!");
        AssertUtil.isBlank(definition.getVersion(), "【" + flowName + "】流程version为空!");
        Long id = IdUtils.snowflakeId();
        //发布
        definition.setIsPublish(1);
        definition.setUpdateTime(new Date());
        definition.setId(id);

        List<FlowNode> nodeList = definition.getNodeList();
        //每一个流程的开始结点个数
        int startNum = 0;
        Set<String> nodeCodeSet = new HashSet<String>();
        //便利一个流程中的各个结点
        for (FlowNode node : nodeList)
        {
            initNodeAndCondition(node, id, definition.getVersion());
            if (NodeType.start.getKey().equals(node.getNodeType())) {
                startNum++;
                AssertUtil.isFalse(startNum > 1, "[" + flowName + "]" + FlowConstant.MUL_START_NODE);
            }
            //保证不存在重复的nodeCode
            AssertUtil.isFalse(nodeCodeSet.contains(node.getNodeCode()),
                    "【" + flowName + "】" + FlowConstant.SAME_NODE_CODE);
            nodeCodeSet.add(node.getNodeCode());
            allNodes.add(node);
            allSkips.addAll(node.getSkipList());
        }

        AssertUtil.isFalse(startNum == 0, "[" + flowName + "]" + FlowConstant.LOST_START_NODE);
        //校验所有目标结点是否都存在
        validaIsExistDestNode(allSkips, nodeCodeSet);
        return combine;
    }

    private static FlowDefinition getFlowJson() {
        FlowDefinition definition = new FlowDefinition();
        definition.setFlowCode("leaveFlow");
        definition.setFlowName("请假流程");
        definition.setVersion("11.0");

        List<FlowNode> nodeList = new ArrayList<>();
        definition.setNodeList(nodeList);

        FlowNode start = new FlowNode();
        nodeList.add(start);
        start.setNodeType(NodeType.getKeyByValue("start"));
        start.setNodeCode("1");
        start.setNodeName("待审批");
        List<FlowSkip> startSkipList = new ArrayList<>();
        FlowSkip startSkip = new FlowSkip();
        startSkip.setNextNodeCode("2");
        startSkipList.add(startSkip);
        start.setSkipList(startSkipList);

        FlowNode end = new FlowNode();
        nodeList.add(end);
        end.setNodeType(NodeType.getKeyByValue("end"));
        end.setNodeCode("5");
        end.setNodeName("审批完成");
        end.setSkipList(new ArrayList<>());

        FlowNode betweenNode1 = new FlowNode();
        nodeList.add(betweenNode1);
        betweenNode1.setNodeType(NodeType.getKeyByValue("between"));
        betweenNode1.setNodeCode("2");
        betweenNode1.setNodeName("组长审批");
        List<FlowSkip> betweenSkipList1 = new ArrayList<>();

        FlowSkip between1StartSkip1 = new FlowSkip();
        between1StartSkip1.setConditionValue("审批通过");
        between1StartSkip1.setNextNodeCode("3");
        betweenSkipList1.add(between1StartSkip1);

        FlowSkip between1StartSkip2 = new FlowSkip();
        between1StartSkip2.setConditionValue("驳回");
        between1StartSkip2.setNextNodeCode("1");
        betweenSkipList1.add(between1StartSkip2);

        betweenNode1.setSkipList(betweenSkipList1);

        FlowNode betweenNode2 = new FlowNode();
        nodeList.add(betweenNode2);
        betweenNode2.setNodeType(NodeType.getKeyByValue("between"));
        betweenNode2.setNodeCode("3");
        betweenNode2.setNodeName("部门经理审批");
        List<FlowSkip> betweenSkipList2 = new ArrayList<>();

        FlowSkip between2StartSkip1 = new FlowSkip();
        between2StartSkip1.setConditionValue("审批通过");
        between2StartSkip1.setNextNodeCode("4");
        betweenSkipList2.add(between2StartSkip1);

        FlowSkip between2StartSkip2 = new FlowSkip();
        between2StartSkip2.setConditionValue("驳回");
        between2StartSkip2.setNextNodeCode("1");
        betweenSkipList2.add(between2StartSkip2);

        betweenNode2.setSkipList(betweenSkipList2);

        FlowNode betweenNode3 = new FlowNode();
        nodeList.add(betweenNode3);
        betweenNode3.setNodeType(NodeType.getKeyByValue("between"));
        betweenNode3.setNodeCode("4");
        betweenNode3.setNodeName("hr审批");
        List<FlowSkip> betweenSkipList3 = new ArrayList<>();

        FlowSkip between3StartSkip1 = new FlowSkip();
        between3StartSkip1.setConditionValue("审批通过");
        between3StartSkip1.setNextNodeCode("5");
        betweenSkipList3.add(between3StartSkip1);

        FlowSkip between3StartSkip2 = new FlowSkip();
        between3StartSkip2.setConditionValue("驳回");
        between3StartSkip2.setNextNodeCode("1");
        betweenSkipList3.add(between3StartSkip2);

        betweenNode3.setSkipList(betweenSkipList3);

        return definition;
    }

    /**
     * 校验所有的目标结点是否存在
     *
     * @param allSkips
     * @param nodeCodeSet
     */
    private static void validaIsExistDestNode(List<FlowSkip> allSkips, Set<String> nodeCodeSet) {
        for (int i = 0; i < allSkips.size(); i++) {
            String nextNodeCode = allSkips.get(i).getNextNodeCode();
            AssertUtil.isFalse(!nodeCodeSet.contains(nextNodeCode), "【" + nextNodeCode + "】" + FlowConstant.NULL_NODE_CODE);
        }
    }


    /**
     * 读取工作结点和跳转条件
     *
     * @param node
     * @param definitionId
     * @param version
     * @return
     */
    private static void initNodeAndCondition(FlowNode node, Long definitionId, String version) {
        String nodeName = node.getNodeName();
        String nodeCode = node.getNodeCode();
        List<FlowSkip> skipList = node.getSkipList();
        if (!NodeType.end.getKey().equals(node.getNodeType())) {
            AssertUtil.isFalse(CollectionUtils.isEmpty(skipList), "开始和中间结点必须有跳转规则");
        }
        AssertUtil.isBlank(nodeCode, "[" + nodeName + "]" + FlowConstant.LOST_NODE_CODE);

        node.setId(IdUtils.snowflakeId());
        node.setVersion(version);
        node.setDefinitionId(definitionId);
        node.setUpdateTime(new Date());

        //跳转条件的集合
        Set<String> conditionSet = new HashSet<>();
        //目标结点的集合 这两个集合都不能重复
        Set<String> targetSet = new HashSet<>();
        //遍历结点下的跳转条件
        for (int i = 0; i < skipList.size(); i++) {
            FlowSkip skip = skipList.get(i);
            skip.setId(IdUtils.snowflakeId());
            //流程id
            skip.setDefinitionId(definitionId);
            //结点id
            skip.setNodeId(node.getId());
            //起始结点
            skip.setNowNodeCode(nodeCode);
            //目标结点
            String target = skip.getNextNodeCode();
            AssertUtil.isBlank(target, "【" + nodeName + "】" + FlowConstant.LOST_DEST_NODE);
            AssertUtil.isFalse(targetSet.contains(target), "[" + nodeName + "]" + FlowConstant.SAME_DEST_NODE);
            targetSet.add(target);
            String value = skip.getConditionValue();
            AssertUtil.isFalse(conditionSet.contains(value), "[" + nodeName + "]" + FlowConstant.SAME_CONDITION_VALUE);
            conditionSet.add(value);
        }
    }

}
